package com.fenghongzhang.lib_core.base

import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ViewModel
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.lifecycle.viewModelScope
import com.fenghongzhang.lib_core.event.TokenInvalidEvent
import com.fenghongzhang.lib_core.exception.ApiCode
import com.fenghongzhang.lib_core.exception.ApiException
import com.fenghongzhang.lib_core.model.BaseOpenApiModel
import com.fenghongzhang.lib_core.post.postEvent
import com.jczy.cyclone.mmkv.AuthMMKV
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch

open class BaseViewModel : ViewModel() {

    //收集流
    fun <M : BaseOpenApiModel> executeOpenApi(
        action: Flow<M>,
        onStart: (suspend () -> Unit)? = null,
        onError: (suspend (BaseOpenApiModel) -> Unit)? = null,
        onFinished: (suspend (M) -> Unit)? = null
    ){
        viewModelScope.launch {
            action.onStart {
                onStart?.invoke()
            }.handleOpenApiException2Model().collect {
                if (it.success) {
                    onFinished?.invoke(it)
                } else {
                    if (isOpenApiReLoginCode(it.code)) {
                        it.detail = "请重新登录"
                        AuthMMKV.accessToken = ""
                        //发送发送token无效事件
                        postEvent(TokenInvalidEvent())
                    }
                    it.setDetailIfEmpty()
                    onError?.invoke(it)
                }
            }
        }

    }

    fun <M : BaseModel<T>, T> execute(
        action: Flow<M>,
        onStart: (suspend () -> Unit)? = null,
        onError: (suspend (String) -> Unit)? = null,
        onFinished: (suspend (M) -> Unit)? = null,
        handleApiExceptionBySelf: Boolean = false
    ) = viewModelScope.launch {
        action.onStart {
            onStart?.invoke()
        }.handleApiException2Model(handleApiExceptionBySelf).collect {
            if (it.code == ApiCode.OK) {
                //完成网络请求的处理
                onFinished?.invoke(it)
            } else {
                if (it.code == ApiCode.LOGIN_TIMEOUT) {
                    AuthMMKV.accessToken = ""
                    //发送发送token无效事件
                    postEvent(TokenInvalidEvent())
                    if (it.msg == "令牌不能为空") {
                        onError?.invoke("")
                        return@collect
                    }
                }
                onError?.invoke(it.msg)
            }
        }
    }

}

inline fun <reified T> Flow<T>.observeWithLifecycle(
    fragment: Fragment,
    minState: Lifecycle.State = Lifecycle.State.STARTED,
    noinline action: suspend (T) -> Unit,
): Job = fragment.viewLifecycleOwner.lifecycleScope.launch {
    flowWithLifecycle(fragment.viewLifecycleOwner.lifecycle, minState).collectLatest {
        action.invoke(it)
    }
}

//把所有异常包装为Model,继续向下传递后续自己处理
fun <M : BaseModel<T>, T> Flow<M>.handleApiException2Model(handleApiExceptionBySelf: Boolean = false) =
    flowOn(Dispatchers.IO).onEach { model ->
        if (model.code != ApiCode.OK && !handleApiExceptionBySelf) {
            throw  ApiException.getApiException(model.code, model.msg)
        }
    }.catch { e ->
        e.printStackTrace()
        val apiException = if (e !is ApiException) {
            ApiException.getApiException(e)
        } else {
            e
        }
        emit(BaseModel(apiException.code, apiException.message, null) as M)
    }.flowOn(Dispatchers.Main)

//判断openApi token为空，重新登录的ApiCode
private fun isOpenApiReLoginCode(code: String?): Boolean {
    return code == "RELOGIN"
}

//把所有异常包装为Model,继续向下传递后续自己处理
fun <M : BaseOpenApiModel> Flow<M>.handleOpenApiException2Model() =
    flowOn(Dispatchers.IO).onEach {
        //不再处理api code异常的问题，让下游自己处理
    }.catch { e ->
        e.printStackTrace()
        val apiException = if (e !is ApiException) {
            ApiException.getApiException(e)
        } else {
            e
        }
        //请求异常，返回baseModel
        emit(BaseOpenApiModel(success = false, detail = apiException.message, message = apiException.message) as M)

    }.flowOn(Dispatchers.Main)




inline fun <reified T> Flow<T>.observeWithLifecycle(
    lifecycleOwner: LifecycleOwner,
    minState: Lifecycle.State = Lifecycle.State.STARTED,
    noinline action: suspend (T) -> Unit
): Job = lifecycleOwner.lifecycleScope.launch {
    flowWithLifecycle(lifecycleOwner.lifecycle, minState).collectLatest {
        action.invoke(it)
    }
}